package com.xm.transfer.factory;


import com.xm.transfer.annotations.*;
import com.xm.transfer.utils.ClassUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanFactory {

    private static HashMap<String, Object> beans = new HashMap<>();

    private BeanFactory() {

    }


    // 加载xml文件，并解析
    static {
        System.out.println("bean starting.");
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        if (null != resourceAsStream) {
            SAXReader saxReader = new SAXReader();
            try {
                Document read = saxReader.read(resourceAsStream);
                Element rootElement = read.getRootElement();
                List<Element> beanList = rootElement.selectNodes("//bean");
                // 实例化bean
                for (int i = 0; i < beanList.size(); i++) {
                    Element element = beanList.get(i);
                    String name = element.attributeValue("name");
                    String aClass = element.attributeValue("class");
                    beans.put(name, Class.forName(aClass).newInstance());
                    System.out.println("create bean:name=" + name + ",class=" + aClass);
                }
                // 维护对象依赖关系
                List<Element> propertyList = rootElement.selectNodes("//property");
                for (int i = 0; i < propertyList.size(); i++) {
                    Element element = propertyList.get(i);
                    String name = element.attributeValue("name");
                    String ref = element.attributeValue("ref");
                    Element parent = element.getParent();
                    String parentName = parent.attributeValue("name");
                    Object parentBean = beans.get(parentName);
                    Method[] methods = parentBean.getClass().getMethods();
                    for (int j = 0; j < methods.length; j++) {
                        String methodName = methods[j].getName();
                        if (("set" + name).equals(methodName)) {
                            methods[j].invoke(parentBean, beans.get(ref));
                            System.out.println("init bean:" + "method=set" + name + ",Object=" + ref);
                        }
                    }

                }
                // 是否开启注解;
                List<Element> annotationList = rootElement.selectNodes("//annotation");
                if (!annotationList.isEmpty()) {
                    System.out.println("enable annotation.");
                    Element element = annotationList.get(annotationList.size() - 1);
                    String enable = element.attributeValue("enable");
                    boolean flag = Boolean.parseBoolean(enable);
                    if (flag) {
                        // 开启注解,实例化
                        String aPackage = element.attributeValue("scanPackage");
                        List<Class<?>> classList = ClassUtils.finds(aPackage);
                        for (int i = 0; i < classList.size(); i++) {
                            Class<?> aClass = classList.get(i);
                            if (aClass.isAnnotation() || aClass.isInterface() || aClass.isEnum()) {
                                // 注解，接口，枚举，不管理
                                continue;
                            }
                            // class 注解实例化
                            String beanName = aClass.getSimpleName();
                            Annotation[] annotations = aClass.getAnnotations();
                            for (int j = 0; j < annotations.length; j++) {
                                Annotation annotation = annotations[j];
                                if (annotation instanceof Component) {
                                    System.out.println("Component annotation create bean:" + beanName);
                                    Component beanAnnotation = aClass.getAnnotation(Component.class);
                                    String value = beanAnnotation.value();
                                    if (null != value && !"".equals(value)) {
                                        beanName = value;
                                    }
                                } else if (annotation instanceof Service) {
                                    System.out.println("Service annotation create bean:" + beanName);
                                    Service beanAnnotation = aClass.getAnnotation(Service.class);
                                    String value = beanAnnotation.value();
                                    if (null != value && !"".equals(value)) {
                                        beanName = value;
                                    }
                                } else if (annotation instanceof Bean) {
                                    System.out.println("Bean annotation create bean:" + beanName);
                                    Bean beanAnnotation = aClass.getAnnotation(Bean.class);
                                    String value = beanAnnotation.value();
                                    if (null != value && !"".equals(value)) {
                                        beanName = value;
                                    }
                                }  else {
                                    System.out.println("not manage " + beanName);
                                    continue;
                                }
                                Object auto = aClass.newInstance();
                                beans.put(beanName, auto);
                                Class<?>[] interfaces = aClass.getInterfaces();
                                for (int k = 0; k < interfaces.length; k++) {
                                    Class<?> anInterface = interfaces[k];
                                    if (null != anInterface.getSimpleName() && !"".equals(anInterface.getSimpleName())) {
                                        System.out.println("interface name bean:" + anInterface.getSimpleName() + "=" + beanName);
                                        beans.put(anInterface.getSimpleName(), auto);
                                    }
                                }
                            }
                        }


                        // 注入对象
                        for (Map.Entry<String, Object> entry : beans.entrySet()) {
                            Object bean = entry.getValue();
                            Field[] declaredFields = bean.getClass().getDeclaredFields();
                            for (int j = 0; j < declaredFields.length; j++) {
                                Field field = declaredFields[j];
                                Annotation[] fieldAnnotations = field.getAnnotations();
                                for (int k = 0; k < fieldAnnotations.length; k++) {
                                    Annotation fieldAnnotation = fieldAnnotations[k];
                                    if (fieldAnnotation instanceof Autowired) {
                                        String name = field.getType().getSimpleName();
                                        System.out.println("Autowired di bean：" + name);
                                        Autowired beanAnnotation = field.getAnnotation(Autowired.class);
                                        String value = beanAnnotation.value();
                                        if (null != value && !"".equals(value)) {
                                            name = value;
                                        }
                                        Object obj = beans.get(name);
                                        if (null == obj) {
                                            System.out.println("not found bean:" + name);
                                        } else if (null == bean) {
                                            System.out.println("not manage bean：" + entry.getKey());
                                        } else {
                                            field.setAccessible(true);
                                            field.set(bean, obj);
                                        }
                                    }

                                }
                            }
                            beans.put(entry.getKey(), bean);
                        }


                        // 事务拦截,代理器代理service
                        for (Map.Entry<String, Object> entry : beans.entrySet()) {
                            Object value = entry.getValue();
                            Method[] methods = value.getClass().getMethods();
                            for (int i = 0; i < methods.length; i++) {
                                Method method = methods[i];
                                Annotation[] annotations = method.getAnnotations();
                                for (int j = 0; j < annotations.length; j++) {
                                    Annotation annotation = annotations[j];
                                    if (annotation instanceof Transactional) {
                                        Object target = ((ProxyFactory) beans.get(ProxyFactory.class.getSimpleName())).getJDKProxy(value);
                                        beans.put(entry.getKey(),target);
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (DocumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } else {
            throw new RuntimeException("请配置bean文件");
        }
        System.out.println("bean loading end.");
    }

    public static <E> E getBean(String name) {
        Object o = beans.get(name);
        return (E) o;
    }

    public static <E> E getBean(Class<?> clazz) {
        return (E) beans.get(clazz.getSimpleName());
    }
}
